#include "OrbitHandler.h"
#include <SECore/Events/Input.h>
#include <SEUi/Widget/Widget.h>
#include <SEMath/Util/Util.h>
#include <SECore/EventDispatcher.h>

#include <SEDebug/Log/Log.h>

namespace SEngine
{
    OrbitHandler::OrbitHandler(EnableEventHandle & enHandle, Camera & camera)
        : CameraEventHandler(enHandle, camera)
    {
        camera.SetPos(glm::vec3{0.0f});
        camera.SetFront(-camera.GetPos());
        m_targetPos = glm::vec3{20.0f, 20.0f, 20.0f};
    }

    void OrbitHandler::OnEvent(EventPtr event)
    {
        EventDispatcher dispatcher(event);
        dispatcher.Dispatcher<MousePressEvent>(std::bind(&OrbitHandler::OnMousePressEvent, this, std::placeholders::_1));
        dispatcher.Dispatcher<MouseReleaseEvent>(std::bind(&OrbitHandler::OnMouseReleaseEvent, this, std::placeholders::_1));
        dispatcher.Dispatcher<MouseMoveEvent>(std::bind(&OrbitHandler::OnMouseMoveEvent, this, std::placeholders::_1));
        dispatcher.Dispatcher<MouseWheelEvent>(std::bind(&OrbitHandler::OnMouseWheelEvent, this, std::placeholders::_1));
    }

    void OrbitHandler::OnUpdate(int delta)
    {
        m_delta = (float)delta;
        auto center = m_camera.GetPos() + m_camera.GetFront();
        float length1 = glm::distance(center, m_camera.GetPos());
        float length2 = glm::distance(center, m_targetPos);
        if (glm::abs(length1 - length2) < 0.001f) {
            glm::vec3 dir = glm::normalize(glm::mix(m_camera.GetPos(), m_targetPos, 0.1f));
            m_camera.SetPos(dir * (length1 + length2) * 0.5f);
        } else {
            m_camera.SetPos(glm::mix(m_camera.GetPos(), m_targetPos, 0.1f));
        }
        m_camera.SetFront(center - m_camera.GetPos());
    }

    void OrbitHandler::OnMousePressEvent(MousePressEventPtr)
    {

    }

    void OrbitHandler::OnMouseReleaseEvent(MouseReleaseEventPtr)
    {

    }
    
    void OrbitHandler::OnMouseMoveEvent(MouseMoveEventPtr e)
    {   
        auto pos_ = Input::GetInstance().GetMousePos();
        glm::vec2 pos{pos_.first, pos_.second};
        if (static_cast<Widget&>(m_enHandle).IsHoveredWindow()) {
            if (e->GetButton() == MouseButton::SE_LEFT) {
                auto offset = pos - m_lastPos;
                float rotateSpeed = m_rotateSpeed * m_delta;
                auto camPos = CartesianToSpherical(m_camera.GetPos());
                camPos.phi += glm::radians(-offset.x * rotateSpeed);
                camPos.theta = glm::clamp(camPos.theta + glm::radians(-offset.y * rotateSpeed), glm::radians(10.0f), glm::radians(170.0f));
                m_targetPos = SphericalToCartesian(camPos);
            }
        }
        m_lastPos = pos;
    }

    void OrbitHandler::OnMouseWheelEvent(MouseWheelEventPtr)
    {
        if (static_cast<Widget&>(m_enHandle).IsHoveredWindow()) {
            auto distance = glm::length(m_camera.GetFront());
            auto dir = -glm::normalize(m_camera.GetFront());
            float wheelx = 0, wheely = 0;
            std::tie(wheelx, wheely) = Input::GetInstance().GetMouseWheelXY();
            distance = glm::clamp(distance - wheely * 10.0f, 10.0f, 100.0f);
            m_targetPos = dir * distance;
        }
    }
}
